//
// Created by Sugars Mac on 2020/5/27.
//
#include <stdio.h>
#include <stdlib.h>     // exit
#include <string.h>
#include <unistd.h>     // bind listen
#include <time.h>       // time(NULL)   ctime(&ticks)
#include <pthread.h>
#include "Datars.h"

char * help_message = "使用命令进行操作: {}中是必填内容，[]中是可选内容\n"
                      "\t\tLSUS -- 查看所有已登陆用户\n"
                      "\t\t[TO] {username} -- 与用户username建立私聊关系\n"
                      "\t\tREPL {message} -- 回复跟您私聊的用户\n"
                      "\t\tQUIT -- 用于在建立了私聊关系后主动退出\n"
                      "\t\tLSRM -- 列举所有聊天室名称\n"
                      "\t\tJOIN {roomname} [password] -- 加入聊天室\n"
                      "\t\tOUTR -- 退出聊天室\n"
                      "\t\tCRRM {roomname} [password] -- 创建聊天室\n"
                      "\t\tDISC -- 结束程序\n"
                      "\t\tHELP -- 显示帮助\n";

char * room_help_message = "您当前处于聊天室中，可使用以下命令：\n"
                           "\t\tMEMB -- 查看聊天室中的用户\n"
                           "\t\tREPL {message} -- 回复私聊消息\n"
                           "\t\tOUTR -- 离开房间\n"
                           "\t\tDISC -- 退出程序\n";

char * alarm_message = "Command Lack of parameter!\n";
/**
 * 处理用户n的数据收发
 * @param n
 */
void rcv_snd(int n)
{
    int len, i;
    char name[NAMELEN], buf[BUFFSIZE], pass[PASSLEN];

    write(users[n].sockfd, "your name: ", strlen("your name: "));
    // 获取用户登陆信息
    user_login(n);
    // 获取用户昵称
//    while(1) {
//        len = read(users[n].sockfd, name, NAMELEN);
//        if (len > 0) {
//            name[len - 1] = '\0';     //
//            if (findUserSockByName(users, MAXMEM, name) != -1){
//                DEBUG("Name exist\n");
//                write(users[n].sockfd, "the name is existed, Reinput: ", strlen("the name is existed, Reinput: "));
//                continue;
//            }
//            users[n].username = malloc(sizeof(char) * len);
//            strcpy(users[n].username, name);
//            DEBUG("DEBUG: New user:%s\n", users[n].username);
//            break;
//        } else {
//            write(users[n].sockfd, "Input your name: ", strlen("Input your name: "));
//        }
//    }

    strcpy(buf, users[n].username);
    strcat(buf, "\tjoin in\n\0");
    //
    for(i=0; i<MAXMEM; i++)
    {
        if(users[i].sockfd != -1)
            write(users[i].sockfd, buf, strlen(buf)); // 给每一个客户端都发送消息
    }

    while(1)   // 命令模式
    {
        char temp[BUFFSIZE];

        int cmdType = processUserInput(n, temp);
        switch (cmdType) {
            case QUIT:
                GroupMessage(n, temp, MAXMEM);
                break;
            case PRIV:
                privChat(n, temp);
                break;
            case REPL:
                replyTo(n, temp);
                break;
            case DISC:
                userDisconnect(n);
                return; // 结束程序
            case HELP:
                showHelp(n);
                break;
            case CRRM:
                createRoom(n, temp);
                break;
            case JOIN:
                joinRoom(n, temp);
                break;
            case LSUS:
                showLoginUsers(n);
                break;
            case LSRM:
                showAllRooms(n);
                break;
            case EMPT:
                break;
            default:
                write(users[n].sockfd, "ERROR COMMAND\n", 15);
                break;
        }
    }
}

//TODO:服务器主动断开客户端
void user_login(int userIndex){
    char name[NAMELEN], pass[PASSLEN], buf[BUFFSIZE];
    memset(name, 0, NAMELEN);
    memset(pass, 0, PASSLEN);
    int sock = users[userIndex].sockfd; // 客户端套接字
    int len = read(sock, name, NAMELEN); // 读取用户名

    DEBUG("read() reture : %d, strlen(name) = %lu\n", len, strlen(name));

    if (len > 0) {
        UserTable_t * userInfo = findUserByName(head, name);
        if (userInfo == NULL) {
            // 新用户注册
            strcpy(buf, "欢迎新用户！请输入密码: ");
            write(sock, buf, strlen(buf));
            len = read(sock, pass, PASSLEN);

            DEBUG("the lenght of pass:%lu\n", strlen(pass));

            if (strlen(pass) >= 3) {
                memset(buf, 0, BUFFSIZE);
                strcpy(buf, "开始使用聊天室吧！，使用HELP命令显示帮助\n");
                write(sock, buf, strlen(buf));
                // 将新用户信息保存到文件
                UserTable_t * u1 = (UserTable_t*)malloc(sizeof(UserTable_t));
                memset(u1->username, 0, sizeof(u1->username));
                strcpy(u1->username, name);
                memset(u1->password, 0, sizeof(u1->password));
                strcpy(u1->password, pass);
                insertTable(head, u1);
                writeToFile(head);
                // 保存到登陆用户
                users[userIndex].username = malloc(sizeof(char) * len);
                strcpy(users[userIndex].username, name);

            } else {
                memset(buf, 0, BUFFSIZE);
                strcpy(buf, "密码太短！至少三个字符\n");
                write(sock, buf, strlen(buf));
                write(sock, "DISC", 4);
                userDisconnect(userIndex);
            }
        } else {
            // 老用户登陆
            strcpy(buf, "欢迎");
            strcat(buf, name);
            strcat(buf, ",请输入密码: ");
            write(sock, buf, strlen(buf));
            len = read(sock, pass, PASSLEN);
            if (strlen(pass) >= 3) {
                if (strcmp(pass, userInfo->password) == 0) {
                    memset(buf, 0, BUFFSIZE);
                    strcpy(buf, "登陆成功！\n");
                    write(sock, buf, strlen(buf));
                    // 保存当前登陆用户信息到内存
                    users[userIndex].username = malloc(sizeof(char) * len);
                    strcpy(users[userIndex].username, name);
                } else {
                    memset(buf, 0, BUFFSIZE);
                    strcpy(buf, "密码错误!\n");
                    write(sock, buf, strlen(buf));
                    write(sock, "DISC", 4); // 断开客户端
                    userDisconnect(userIndex);
                }
            } else {
                memset(buf, 0, BUFFSIZE);
                strcpy(buf, "密码太短！至少三个字符\n");
                write(sock, buf, strlen(buf));
                write(sock, "DISC", 4);
                userDisconnect(userIndex);
            }
        }
    } else {
        memset(buf, 0, BUFFSIZE);
        strcpy(buf, "用户名不能为空！\n");
        write(sock, buf, strlen(buf));
        write(sock, "DISC", 4);
        userDisconnect(userIndex);
    }
}

/**
 * tested
 */
int processUserInput(int userIndex, char* buf){
    memset(buf, 0, BUFFSIZE);
    char cmd[5];
    int len;
    int sockfd = users[userIndex].sockfd;
    if((len=read(sockfd, buf, BUFFSIZE)) > 0){
//        buf[len-1]='\0'; // 由于客户端发送的信息除去了换行符
        strncpy(cmd, buf, 4);
        cmd[4] = '\0';
        if(strcmp(cmd, "[TO]") == 0){
            return PRIV; // 申请私聊
        } else if(strcmp(cmd, "DISC") == 0){
            return DISC; // 断开链接
        } else if(strcmp(cmd, "QUIT") == 0){
            return QUIT; // 结束私聊
        } else if(strcmp(cmd, "REPL") == 0){
            return REPL; // 回复私聊
        } else if (strcmp(cmd, "OUTR") == 0){
            return OUTR; // 退出群聊
        } else if(strcmp(cmd, "CRRM") == 0){
            return CRRM;
        } else if (strcmp(cmd, "JOIN") == 0) {
            return JOIN;
        } else if (strcmp(cmd, "HELP") == 0){
            return HELP;
        } else if(strcmp(cmd, "LSUS") == 0) {
            return LSUS;
        } else if (strcmp(cmd, "LSRM") == 0){
            return LSRM;
        } else if (strcmp(cmd, "MEMB") == 0){
            return MEMB;
        } else {
            return GENE;
        }
    } else {
        return EMPT;
    }
}

/**
 * 将用户要发的
 * @param userIndex
 * @param mess
 * @param roomId 为MAXMEM时，发给所有登陆用户
 */
void GroupMessage(int userIndex, char *mess, int roomId){
    time_t ticks;
    char mytime[32], buf[BUFFSIZE];
    // 拼接时间、发送者姓名等信息
    ticks = time(NULL);
    sprintf(mytime, "%.24s\r\n", ctime(&ticks));
    strcpy(buf, users[userIndex].username);
    strcat(buf, "\t");
    strcat(buf, mytime);
    strcat(buf, "\r\t");
    strcat(buf, mess);
    strcat(buf, "\n");
    int i;
    for(i=0; i<MAXMEM; i++)
    {
        if(users[i].sockfd != -1 && (users[i].groupid == roomId || roomId == MAXMEM))
            write(users[i].sockfd, buf, strlen(buf)); // 群发
    }
}

void userDisconnect(int userIndex){
    int ret = 1;
    // 删除建立起的私聊关系
    deleteRelationBySocket(list, users[userIndex].sockfd);
    // 关闭套接字
    close(users[userIndex].sockfd);
    // 消除登陆用户数据
    users[userIndex].sockfd = -1;
    free(users[userIndex].username);
    users[userIndex].username = NULL;
    users[userIndex].groupid = -1;

    pthread_exit(&ret);
}

/**
 *  用户私聊请求
 * @param userIndex : 请求者用户A的socket
 * @param rela_mess : "[TO] anotherUserName"
 */
void privChat(int userIndex, char* rela_mess){
    // 判断是否存在用户B
    char * p = strtok(rela_mess, " ");
    p = strtok(NULL, " "); // 此时p指向用户B
    DEBUG("p = %s", p);
    int sockB = findUserSockByName(users, MAXMEM, p);
    if(sockB == users[userIndex].sockfd){
        write(users[userIndex].sockfd, "you can talk to yourself...\n", 30);
        return;
    }
    if(sockB == -1){
        char buf[BUFFSIZE];
        strcpy(buf, p);
        strcat(buf, " doesn't exists!\n");
        write(users[userIndex].sockfd, buf, strlen(buf));
        return;
    } else {
        int sockA = users[userIndex].sockfd;
        relationMap * rel = buildRelation(sockA, sockB);
        char msg_inform[BUFFSIZE];
        if(findRelation(list, rel) == 1){
            // 已经建立了私聊关系（B->A)
            strcpy(msg_inform, "您已经和");
            strcat(msg_inform, p);
            strcat(msg_inform, "建立了私聊关系\n");
            write(sockA, msg_inform, strlen(msg_inform));
            return;
        }
        if (beContacted(list, sockB) != NULL){ // B已经被别人建立了私聊请求
            strcpy(msg_inform, "目标用户");
            strcat(msg_inform, p);
            strcat(msg_inform, "已经被别人的私聊请求，请稍后再试\n");
            write(sockA, msg_inform, strlen(msg_inform));
            return;
        }
        if (existRelation(list, sockB) != NULL) {
            // 目标用户B已经和别人建立起了私聊
            strcpy(msg_inform, "目标用户");
            strcat(msg_inform, p);
            strcat(msg_inform, "已经向别人发起了私聊请求，请稍后再试\n");
            write(sockA, msg_inform, strlen(msg_inform));
            return;
        }
        strcpy(msg_inform, users[userIndex].username);
        strcat(msg_inform, "向您发起了私聊请求, 你可以使用\"REPL\" + 你要发送的消息 进行回复\n");
        write(sockB, msg_inform, strlen(msg_inform));

        // 建立 A->B 的私聊关系
        addRelation(list, rel);

        memset(msg_inform, 0, BUFFSIZE);
        strcpy(msg_inform, "进入私聊模式，使用QUIT命令退出\n");
        write(sockA, msg_inform, strlen(msg_inform));

        char buffer[BUFFSIZE];
        char temp[BUFFSIZE];
        int size;
        while (1){
            int ret = processUserInput(userIndex, buffer);
            if(ret == QUIT){
                // 通知A、B结束私聊
                write(sockA, "私聊结束!\n", 10);
                memset(temp, 0, BUFFSIZE);
                strcpy(temp, users[userIndex].username);
                strcat(temp, " ended the private chat\n");
                write(sockB, temp, strlen(temp));
                // 修改私聊建立记录
                deleteRelation(list, rel);
                break;
            } else if(ret == DISC){
                memset(temp, 0, BUFFSIZE);
                strcpy(temp, users[userIndex].username);
                strcat(temp, " ended the private chat\n");
                write(sockB, temp, strlen(temp));
                // 修改私聊建立记录
                deleteRelation(list, rel);
                userDisconnect(userIndex);
                break;
            } else {
                memset(temp, 0, BUFFSIZE);
                strcpy(temp, users[userIndex].username);
                strcat(temp, " Say To You: \n\t\t");
                strcat(temp, buffer);
                strcat(temp, "\nuse REPL to reply\n");
                write(sockB, temp, strlen(temp));
            }
        }
    }
}

/**
 * 用户回复私聊
 * @param userIndex ： 发送Reply信息的用户索引
 * @param rela_mess : "REPL message"
 */
void replyTo(int userIndex, char* rela_mess){
    relationMap * rel = beContacted(list, users[userIndex].sockfd); // 判断该用户是否被别人建立起了私聊关系
    char buffer[BUFFSIZE];
    if(rel == NULL){
        strcpy(buffer, "Private chat Relation is not built yet. Use \"[TO] username\" to build\n");
        write(users[userIndex].sockfd, buffer, strlen(buffer));
        return;
    } else {
        char* p = rela_mess;  // B要发送给A的消息
        p += 4;  // 截取字符串
        strcpy(buffer, users[userIndex].username);
        strcat(buffer, " reply: ");
        strcat(buffer, p);
        strcat(buffer, "\n");
        int sockA = rel->sockA; // 发起私聊请求的用户A
        write(sockA, buffer, strlen(buffer)); // 将b的回复发送给A;
        return;
    }
}

// 显示帮助信息
void showHelp(int userIndex){
    write(users[userIndex].sockfd, help_message, strlen(help_message));
}

// 显示所有以登陆用户
void showLoginUsers(int userIndex){
    int i;
    char buf[BUFFSIZE];
    int count = 0;
    strcpy(buf, "The users:\n");
    for(i = 0; i < MAXMEM; i++){
        if (users[i].sockfd != -1) {
            strcat(buf, users[i].username);
            strcat(buf, "\t");
        }
    }
    strcat(buf, "\n");
    write(users[userIndex].sockfd, buf, strlen(buf));
}

// 显示所有已创建房间
void showAllRooms(int userIndex){
    int i;
    char buf[256];
    strcpy(buf, "Here are the rooms you can join:\n");
    for(i = 0; i < MAXMEM; i++){
        if (rooms[i].roomname != NULL) {
            strcat(buf, "\t");
            strcat(buf, rooms[i].roomname);
            strcat(buf, "\n");
        }
    }
    write(users[userIndex].sockfd, buf, strlen(buf));
};

// 加入房间,rela_mess中包含房间名（密码）
void joinRoom(int userIndex, char* rela_mess){
    char * roomname = malloc(sizeof(char) * NAMELEN);
    char * password = NULL;
    char * p = strtok(rela_mess, " ");
    p = strtok(NULL, " "); // 获取到房间名
    if(p == NULL || strlen(p) >= NAMELEN) { // 缺少房间名参数
        write(users[userIndex].sockfd, alarm_message, strlen(alarm_message));
        return;
    }
    strcpy(roomname, p);
    int roomIndex = findRoomByName(rooms, MAXMEM, roomname);
    if (roomIndex == -1) {
        char buf[BUFFSIZE];
        strcpy(buf, "Wrong Room Name!\n");
        write(users[userIndex].sockfd, buf, strlen(buf));
        return ;
    }
    if (rooms[roomIndex].roompass != NULL) {
        p = strtok(NULL, " ");
        if (p == NULL || strcmp(p, rooms[roomIndex].roompass) != 0) { // 密码错误或者没有
            char buf[BUFFSIZE];
            strcpy(buf, "Wrong password!\n");
            write(users[userIndex].sockfd, buf, strlen(buf));
            return ;
        }
    }

    char join_success_mess[BUFFSIZE];
    strcpy(join_success_mess, "Join In Success!\n");
    strcat(join_success_mess, "Welcome To ");
    strcat(join_success_mess, roomname);
    strcat(join_success_mess, "!\nUse OUTR to quit group chat\n");
    write(users[userIndex].sockfd, join_success_mess, strlen(join_success_mess));

    users[userIndex].groupid = roomIndex;

    inRoom(userIndex); // 处理用户在群中的发言
}

// 创建群聊, rela_mess中包含房间名（密码）
void createRoom(int userIndex, char* rela_mess){
    char * roomname = malloc(sizeof(char) * NAMELEN);
    char * password = NULL;
    char * p = strtok(rela_mess, " ");
    p = strtok(NULL, " "); // 获取到房间名
    if(p == NULL) { // 缺少房间名参数
        write(users[userIndex].sockfd, alarm_message, strlen(alarm_message));
        return;
    }
    strcpy(roomname, p);
    int roomIndex = searchEmptyRoom(rooms, MAXMEM);
    if (roomIndex == -1){
        char * buf = "Oops, run out of Room, Plz try later..\n";
        write(users[userIndex].sockfd, buf, strlen(buf));
        return;
    }
    p = strtok(NULL, " "); // 获取密码
    if (p != NULL){
        password = malloc(sizeof(char) * PASSLEN);
        strcpy(password, p);
    }
    rooms[roomIndex].roomname = roomname;
    rooms[roomIndex].roompass = password;

    char * mess = "Create Room Success!User MEMB to show who is in the Room\n";
    write(users[userIndex].sockfd, mess, strlen(mess));
    users[userIndex].groupid = roomIndex;

    inRoom(userIndex);
}

void inRoom(int userIndex){
    int roomIndex = users[userIndex].groupid;
    if (roomIndex == -1){
        return;
    }
    char buffer[BUFFSIZE];
    char temp[BUFFSIZE];
    while (1){
        int ret = processUserInput(userIndex, buffer);
        if(ret == OUTR){
            memset(temp, 0, BUFFSIZE);
            strcpy(temp, "退出了群聊");
            GroupMessage(userIndex, temp, roomIndex);
            users[userIndex].groupid = -1;  // 清除加入群聊记录
            cleanRoom(roomIndex);
            break;
        } else if (ret == REPL) { // 回复别人的私聊消息
            replyTo(userIndex, buffer);
        } else if (ret == DISC) {  // 其他消息一律视为
            memset(temp, 0, BUFFSIZE);
            strcpy(temp, "退出了群聊");
            GroupMessage(userIndex, temp, roomIndex);
            userDisconnect(userIndex);
            cleanRoom(roomIndex);
            return;
        } else if (ret == HELP){
            write(users[userIndex].sockfd, room_help_message, strlen(room_help_message));
        } else if (ret == MEMB) {
            char buf[BUFFSIZE];
            listUserInRoom(roomIndex, buf);
            write(users[userIndex].sockfd, buf, strlen(buf));
        } else {
            GroupMessage(userIndex, buffer, roomIndex);
        }
    }
}

void listUserInRoom(int roomId, char * buf){
    memset(buf, 0, BUFFSIZE);
    int i;
    strcpy(buf, "The users here: ");
    for(i = 0; i < MAXMEM; i++){
        if (users[i].groupid == roomId) {
            strcat(buf, users[i].username);
            strcat(buf, "\t");
        }
    }
    strcat(buf, "\n");
}

void cleanRoom(int roomId){
    if (rooms[roomId].roomname == NULL){
        return;
    }
    int find = false;
    for (int i = 0; i < MAXMEM; ++i) {
        if (users[i].groupid == roomId) {
            // 有人还在群中
            find = true;
            break;
        }
    }
    if (!find) {
        // 群里已经没人了
        free(rooms[roomId].roomname);
        rooms[roomId].roomname = NULL;
        if (rooms[roomId].roompass) {
            free(rooms[roomId].roompass);
            rooms[roomId].roompass = NULL;
        }
    }
}